from urllib.parse import urlparse
import os
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker, declarative_base

from server.report.config.config import Config
from server.config.singleton import Singleton
from server.config.log import logger
from server.exceptions import db_exception


class MySQLUtil(metaclass=Singleton):
    """
    database connections init and management
    """
    sqlalchemy_engine = None
    session_local = None

    def __init__(self) -> None:
        self.cfg: Config = Config()
        if not self.sqlalchemy_engine:
            MySQLUtil.init_engine(self.cfg)

    @contextmanager
    def get_db(self):
        """
        After you finish using the session, it's crucial to close it.
        """
        if not MySQLUtil.sqlalchemy_engine:
            MySQLUtil.init_engine(self.cfg)
        session = self.session_local()  # type: ignore pylint: disable=not-callable
        try:
            yield session
        finally:
            session.close()

    @staticmethod
    def init_engine(cfg: Config):
        """
        initial engine and session maker Factory
        """
        pool_size = cfg.mysql_pool_size
        if MySQLUtil.sqlalchemy_engine is None:
            if cfg.mysql_type == "mysql":
                db_url = MySQLUtil.create_mysql_url(cfg)
            else:
                logger.error("Unsupported database type %s", cfg.mysql_type)
                exit(-1)
            MySQLUtil.sqlalchemy_engine = create_engine(
                db_url, 
                pool_size=pool_size,
                max_overflow=10,
                pool_recycle=3600,
                pool_pre_ping=True)
            MySQLUtil.session_local = sessionmaker(
                autocommit=False, autoflush=False, bind=MySQLUtil.sqlalchemy_engine)

    @staticmethod
    def create_mysql_url(cfg):
        """
        create and validate MySQL url
        """
        username: str = cfg.mysql_username
        password: str = cfg.mysql_password
        database: str = cfg.mysql_database
        host: str = cfg.mysql_host
        port: str = cfg.mysql_port

        url = f"mysql+pymysql://{username}:{password}@{host}:{port}/{database}?charset=utf8"

        return url


    def db_add_commit_refresh(self, session: Session, what):
        """
        add data to database
        """
        try:
            session.add(what)
            session.commit()
            session.refresh(what)
        except Exception as e:
            logger.exception("db commit error with data %s", str(what.__dict__))
            ex = db_exception()
            ex.detail = str(e)
            session.rollback()
            raise ex from e

    def db_merge_commit(self, session: Session, what):
        try:
            session.merge(what)
            session.commit()
        except Exception as e:
            ex = db_exception()
            ex.detail = str(e)
            logger.exception("db merge commit error with data %s", str(what.__dict__))
            session.rollback()
            raise ex from e

    def db_update_commit_refresh(self, session: Session, existing, what):
        what = what.model_dump(mode="json")
        try:
            for key in what.keys():
                if what[key] is not None:  # 检查b中的字段是否为None
                    setattr(existing, key, what[key])  # 更新a的字段
            session.commit()
            session.refresh(existing)
        except Exception as e:
            ex = db_exception()
            ex.detail = str(e)
            logger.exception("db update commit refresh error with data %s", str(what.__dict__))
            session.rollback()
            raise ex from e


Base = declarative_base()
mysql_util = MySQLUtil()